package org.springframework.beans.factory.xml;

import cn.hutool.core.util.StrUtil;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;

/**
 * @Name XmlBeanDefinitionReader
 * @Description: 从 Xml 配置文件中读取 BeanDefinition 信息的 [实现] 类
 * 描述信息：读取配置在 Xml 文件中的 BeanDefinition 信息。
 * @Version v1.0.0
 * @Author: lichao
 * @Date: 2024-03-15 17:38:22
 **/
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    public static final String BEAN_ELEMENT = "bean";
    public static final String ID_ATTRIBUTE = "id";
    public static final String CLASS_ATTRIBUTE = "class";
    public static final String INIT_METHOD_ATTRIBUTE = "init-method";
    public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";
    public static final String SCOPE_ATTRIBUTE = "scope";
    public static final String LAZYINIT_ATTRIBUTE = "lazy-init";
    public static final String PROPERTY_ELEMENT = "property";
    public static final String NAME_ATTRIBUTE = "name";
    public static final String VALUE_ATTRIBUTE = "value";
    public static final String REF_ATTRIBUTE = "ref";
    public static final String COMPONENT_SCAN_ELEMENT = "component-scan";
    public static final String BASE_PACKAGE_ATTRIBUTE = "base-package";

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
        super(registry, resourceLoader);
    }

    @Override
    public void loadBeanDefinitions(String location) throws BeansException {
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        loadBeanDefinitions(resource);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) throws BeansException {
        try {
            InputStream inputStream = resource.getInputStream();
            try {
                doLoadBeanDefinitions(inputStream);
            } finally {
                inputStream.close();
            }
        } catch (IOException | DocumentException e) {
            throw new BeansException("XML 配置文件 [" + resource + "] 解析失败", e);
        }
    }

    protected void doLoadBeanDefinitions(InputStream inputStream) throws DocumentException {
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        Element root = document.getRootElement();

        // 方式一：基于注解配置的 Bean 容器。
        // 实现原理：解析 <context:component-scan /> 标签，扫描指定包中 @Component 注解的类，提取类信息，组装成 BeanDefinition
        Element componentScan = root.element(COMPONENT_SCAN_ELEMENT);
        if (componentScan != null) {
            String scanPath = componentScan.attributeValue(BASE_PACKAGE_ATTRIBUTE);
            if (StrUtil.isEmpty(scanPath)) {
                throw new BeansException("标签 <context:component-scan /> 中的属性 base-package 值不能为 empty 或 null！");
            }
            scanPackage(scanPath);
        }

        // 方式二：基于 Xml 配置的 Bean 容器。
        // 实现原理：解析 <bean /> 标签，提取类信息，组装成 BeanDefinition
        List<Element> beanList = root.elements(BEAN_ELEMENT);
        for (Element bean : beanList) {
            String beanId = bean.attributeValue(ID_ATTRIBUTE);
            String beanName = bean.attributeValue(NAME_ATTRIBUTE);
            String className = bean.attributeValue(CLASS_ATTRIBUTE);
            String initMethodName = bean.attributeValue(INIT_METHOD_ATTRIBUTE);
            String destroyMethodName = bean.attributeValue(DESTROY_METHOD_ATTRIBUTE);
            String scope = bean.attributeValue(SCOPE_ATTRIBUTE);
            String lazyInit = bean.attributeValue(LAZYINIT_ATTRIBUTE);

            Class<?> clazz;
            try {
                clazz = Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new BeansException("标签 <bean /> 中配置的目标类 [" + beanName + "] 不存在！");
            }
            // id 属性优先于 name 属性进行解析
            beanName = StrUtil.isNotEmpty(beanId) ? beanId : beanName;
            if (StrUtil.isEmpty(beanName)) {
                // 如果 id 和 name 属性都为空，默认将类名的第一个字母转为小写后作为 Bean 实例的名称
                beanName = StrUtil.lowerFirst(clazz.getSimpleName());
            }

            BeanDefinition beanDefinition = new BeanDefinition(clazz);
            beanDefinition.setInitMethodName(initMethodName);
            beanDefinition.setDestoryMethodName(destroyMethodName);
            beanDefinition.setLazyInit(Boolean.parseBoolean(lazyInit));
            if (StrUtil.isNotEmpty(scope)) {
                beanDefinition.setScope(scope);
            }

            List<Element> propertyList = bean.elements(PROPERTY_ELEMENT);
            for (Element property : propertyList) {
                String pName = property.attributeValue(NAME_ATTRIBUTE);
                String pValue = property.attributeValue(VALUE_ATTRIBUTE);
                String pRef = property.attributeValue(REF_ATTRIBUTE);
                if (StrUtil.isEmpty(pName)) {
                    throw new BeansException("标签 <property /> 中配置的 name 属性不能为 empty 或 null！");
                }
                Object value = pValue;
                if (StrUtil.isNotEmpty(pRef)) {
                    value = new BeanReference(pRef);
                }
                PropertyValue propertyValue = new PropertyValue(pName, value);
                beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
            }
            if (getBeanDefinitionRegistry().containsBeanDefinition(beanName)) {
                // Bean 容器中不允许存在重名的 Bean 实例
                throw new BeansException("Bean 实例 [" + beanName + "] 已存在！");
            }
            // 注册 BeanDefinition 信息
            getBeanDefinitionRegistry().registerBeanDefinition(beanName, beanDefinition);
        }
    }

    private void scanPackage(String scanPath) {
        String[] basePackage = StrUtil.splitToArray(scanPath, ',');
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(getBeanDefinitionRegistry());
        scanner.doScan(basePackage);
    }

}

